package pho.prowon.index;

//@TODO (CPA) Backup of index on startup
//@TODO (CPA) File Open to open another index --> set system property and create a new RootIndexCategory 
//@PHO Should this class provide a GUI for Open, New and Save As ?


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import pho.helper.XMLHelper;
import pho.prowon.ImageData;
import pho.prowon.ImageDataContainer;

/**
 * @author Peter
 *
 * This is the XML based version of the Index. It reads its data from an XML
 * file and writes it to an XML file. The default name for the file is index.xml
 * It can be set via the property referenced by the constant INDEX_LOCATION_PROPERTY 
 */

public abstract class IndexXMLImplementation implements IndexImplementation
{

	/**
	 * This is the name of the property referencing the index location (file name)
	 */
	public static final String PROPNAME_INDEXLOCATION = "prowon.IndexLocation";	

	protected ImageData image = null;
	private ArrayList values = new ArrayList ();
	protected ArrayList imageList = new ArrayList ();
	protected Class toBeInstantiated;
	protected Boolean checkIt;
	protected Boolean loadIt;
	protected StringBuffer currentTagContent = new StringBuffer();
	protected String currentTag = "";


	//@TODO Make a backup copy of the index on startup


	/* 
	 * @see pho.prowon.index.IndexImplementation#writeIndex(java.util.List)
	 */
	public void writeIndex (Collection images) 
	{
		try
		{
			writeIndex (images, new FileOutputStream (getDataSource()));
		}
		catch (IOException e)
		{
			System.out.println (e.getMessage());
		}
	}

	/* (non-Javadoc)
	 * @see pho.prowon.index.IndexImplementation#readIndex(java.lang.Class)
	 */
	public List readIndex (Class tBI)
	{
		return readIndex (tBI, Boolean.FALSE);
	}

	/* (non-Javadoc)
	 * @see pho.prowon.index.IndexImplementation#readIndex(java.lang.Class, java.lang.Boolean)
	 */
	public List readIndex (Class tBI, Boolean cI)  
	{
		return readIndex (tBI, cI, Boolean.FALSE);
	}

	/* (non-Javadoc)
	 * @see pho.prowon.index.IndexImplementation#readAndReloadIndex(java.lang.Class)
	 */
	public List readAndReloadIndex (Class tBI)  
	{
		return readIndex (tBI, Boolean.TRUE, Boolean.TRUE);
	}

	private List readIndex (Class tBI, Boolean cI, Boolean lI)  
	{
		boolean classOK = false;
		imageList.clear ();

		// check if the class implements the ImageDataContainer interface
		Class[] interfaces = tBI.getInterfaces();
		for (int i = 0; i < interfaces.length; i++)
		{
			if (interfaces[i]
				.getName()
				.equals("pho.prowon.ImageDataContainer"))
			{
				classOK = true;
			}
		}

		if (!classOK)
		{
			return imageList;
		}

		checkIt = cI;
		loadIt = lI;
		toBeInstantiated = tBI;

		try
		{
			System.out.println ("Loading INDEX from ["+getDataSource()+"]");
			return readIndex (new FileInputStream (getDataSource()));
		}
		catch (IOException e)
		{
			// no index file found
			System.out.println ("No INDEX found ["+getDataSource()+"]- created an empty index");
		}
		return new ArrayList ();
	}

	private void writeIndex (Collection images, OutputStream os) throws java.io.IOException
	{
		Iterator iterator = images.iterator ();
		OutputStreamWriter writer = new OutputStreamWriter (os, Charset.forName("UTF-8"));
		
		// Start XML 
		writer.write ("<"+getRootElement()+">");
			
		while (iterator.hasNext ())
		{
			writer.write( imageDataContainerAsXMLString((ImageDataContainer) iterator.next()));
		}						
		
		writer.write ("</"+getRootElement()+">");
		writer.close();
	}

	protected abstract List readIndex (InputStream is); 

	protected String getDataSource ()
	{
		return System.getProperty(PROPNAME_INDEXLOCATION,"index.xml");
	}

	protected String getRootElement ()
	{
		return "ProwonIndex";
	}

	protected String getImageElement ()
	{
		return "ImageData";
	}
	
	private String imageDataContainerAsXMLString (ImageDataContainer id)
	{
		StringBuffer sb = new StringBuffer ();
		sb.append("<");
		sb.append(getImageElement());
		sb.append(">");					
	    
		sb.append("<FilePath>");					
		sb.append(XMLHelper.xmlChars(id.getPathName()));
		sb.append("</FilePath>");

	    // iterate through tags 
	    Iterator tags = id.getTagNameIterator();
	    while (tags.hasNext()) 
	    {		
	    	String key = (String) tags.next();
	    	
			sb.append("<");
			sb.append(key);					
			sb.append (">");
			if (id.isImageDataTagArray(key))
			{
				String [] values = (String [])id.getImageDataTagArray(key);
				for (int i = 0;i < values.length; i++)
				{
					sb.append("<Value>");					
					sb.append(XMLHelper.xmlChars(values[i]));
					sb.append("</Value>");
				}
			}
			else
			{
				String value = (String)id.getImageDataTag(key);
				sb.append(XMLHelper.xmlChars(value));				
			}
			sb.append("</");
			sb.append(key);					
			sb.append(">");	    	
		}

		sb.append("</"+getImageElement()+">");
		return sb.toString();
	}
	

	/**
	 * 
	 */
	protected void processNewImage()
	{
		boolean includeInIndex = true;
			
		if (checkIt.booleanValue())
		{
			if (! new File(currentTagContent.toString()).exists())
			{
				System.out.println ("[IndexImplementation] Ignoring "+currentTagContent);
				includeInIndex = false;
			}
		}
				
		if (includeInIndex)
		{
			image = new ImageData(currentTagContent.toString(),loadIt);
					
			// invalid images will not be included in the index			
			if (!image.isValid())
			{
				image = null;
				System.out.println ("Ignoring Image "+currentTagContent+" because it cannot be read");
			}
		}						
	}


	/**
	 * 
	 */
	protected void processEndImage()
	{
		if (image != null)
		{
			image.setInitialized();
			try
			{
				ImageDataContainer idc = (ImageDataContainer) toBeInstantiated.newInstance ();
				idc.setImageData(image);
				imageList.add(idc);
			}
			catch (InstantiationException e)
			{
				System.out.println (e.getMessage());
			}
			catch (IllegalAccessException e)
			{
				System.out.println (e.getMessage());
			}					
		}
		values.clear();
	}

	/**
	 * 
	 */
	protected void processEndTag()
	{
		if (image != null && values.size()>0 && !loadIt.booleanValue())
		{
			image.setImageDataTagArray(currentTag, (String []) values.toArray(new String [values.size()]));
		}
		values.clear();
	}
	
	/**
	 * 
	 */
	protected void processValue()
	{
		if (image != null && !loadIt.booleanValue())
		{
			values.add (currentTagContent.toString());
		}
	}

	
	
}
